// Copyright (c) 2009, David Aiken
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the organization to which the authors belong nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL AUTHORS AND CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef __SOP_PRT_h__
#define __SOP_PRT_h__

#include <stdarg.h>
#include <SOP/SOP_Node.h>
#include "PRT.h"

const uint SIZEOF_FLOAT = 4;
const uint SIZEOF_INT = 4;

// file, range, attributes
const uint NUM_GUI_PARMS = 4;

const char* FILE_NAME_PARM =       "file_name";
const char* MISSING_FRAME_PARM =   "missing_frame";
const char* ATTRS_PARM =           "attrs";
const char* RELOAD_GEOMETRY_PARM = "reload_geometry";
const char* MODE_PARM =            "mode";


namespace HDK_PRT
{
    enum SOP_StorageType
    {
        SOP_STORAGETYPE_16,
        SOP_STORAGETYPE_32,
        SOP_STORAGETYPE_64
    };

    enum enumMode
    {
        modeNone = 0,
        modeAutomatic,
        modeRead,
        modeWrite,
        modeNoOperation
    };

    enum enumMissingFrame
    {
        missingFrameNone = 0,
        missingFrameReportError,
        missingFrameNoGeometry
    };

    enum enumExceptionSeverity
    {
        exceptionNone = 0,
        exceptionWarning,
        exceptionError,
        exceptionCritical
    };

    class SOP_PRT_Exception
    {
    private:
        enumExceptionSeverity e_severity;
        va_list e_argptr;
        char e_msg[2056];

    public:
        SOP_PRT_Exception(enumExceptionSeverity severity, std::string fmt, ...)
        :e_severity(severity)
        {
            va_start(e_argptr, fmt);
            vsprintf(e_msg, fmt.c_str(), e_argptr);
            va_end(e_argptr);
        }

        ~SOP_PRT_Exception()
        {
        }

        void output()
        {
            FILE *fp = stderr;
            const char *type = "ERROR";
            if(e_severity == exceptionWarning)
            {
                fp = stdout;
                type = "WARNING";
            }
            fprintf(fp, "\n%s: PRT SOP exception. %s", type, e_msg);
        }

        const char * getMsg()
        {
            return e_msg;
        }

        enumExceptionSeverity getSeverity()
        {
            return e_severity;
        }
    };

    struct SOPAttribute
    {
        std::string mName;
        int mArity;
        DataType mType;
        Array mData;
        bool mConvertTo3DSCoordSpace;

        SOPAttribute(PRTChannel *pch=NULL):
          mName(""), mArity(-1), mType(kUNKNOWN)
        {
            if(pch)
            {
                mName = pch->mName;
                mArity = pch->mArity;
                mType = pch->mType;
                mData = pch->mData;
            }
        }
    };

    //!Reads and writes Krakatoa PRT file format
    class SOP_PRT : public SOP_Node
    {
    public:
        typedef std::map<std::string, SOPAttribute> AttrMap;

        SOP_PRT(OP_Network *net, const char *name, OP_Operator *op);
        virtual ~SOP_PRT();

        static PRM_Template    myTemplateList[];
        static OP_Node *myConstructor(OP_Network*, const char *, OP_Operator *);

    protected:
        //! Cook geometry for the SOP
        virtual OP_ERROR cookMySop(OP_Context &context);

        virtual unsigned int disableParms();

    private:
        bool mCalledFromCallback;

        std::string mSOPVersion;

        enumMissingFrame MISSING_FRAME(float t)
        {
            return enumMissingFrame(evalInt(MISSING_FRAME_PARM, 0, t) + 1);
        }

        enumMode MODE(float t)
        {
            return enumMode(evalInt(MODE_PARM, 0, t) + 1);
        }

        //!Flag an attribute channel for output
        void
        addChannel(unsigned int id, PRT& prt, const SOPAttribute &attr, long long particleCount);

        void
        addOrCreateAttrib(const std::string &attribName, GB_AttribType type, unsigned int arity);

        void
        readFile(PRT &prt, OP_Context &context);

        void
        writeFile(PRT &prt, OP_Context &context);

        //!Callback Fn to handle pushbutton in GUI
        static int
        reloadGeometry(void *data, int index, float time, const PRM_Template *tplate );

        static int
        updateMode(void *data, int index, float time, const PRM_Template *tplate );

        // Functions for GUI widgets
        void FNAME(UT_String &label, float t)
        {
            evalString(label, FILE_NAME_PARM, 0, t);
        }

        int ATTR_COUNT()
        {
            return evalInt(ATTRS_PARM, 0, 0.0f);
        }

        void ATTR_NAME(std::string &label, int attribIndex, float t)
        {
            UT_String val;
            evalStringInst("attrib#", &attribIndex, val, 0, t);
            label = val.toStdString();
        }

        SOP_StorageType ATTR_TYPE(int attribIndex, float t)
        {
            return (SOP_StorageType)evalIntInst("storagetype#", &attribIndex, 0, t);
        }

        bool ATTR_CONVERT(int attribIndex, float t)
        {
            return (bool)evalIntInst("convertTo3DSCoordSpace#", &attribIndex, 0, t);
        }
    };
} // End HDK_PRT namespace

#endif
